package ru.volgogradetzzz.display {
	import ru.volgogradetzzz.geom.UVBox;
	import ru.volgogradetzzz.math.VMatrix2D;
	import ru.volgogradetzzz.math.VVector2;
	
	/**
	 * ...
	 * @author Volgogradetzzz
	 */
	public class DisplayObject2D {
		
		private const _COLORS:Vector.<Number> = new Vector.<Number>(12, true);
		private const _UVS:Vector.<Number> = new Vector.<Number>(8, true);
		
		private const _MODEL_TRANSFORM:VMatrix2D = new VMatrix2D();
		private const _TRANSFORMED_VERTICES:Vector.<Number> = new Vector.<Number>(12, true);
		private const _WORLD_TRANSFORM:VMatrix2D = new VMatrix2D();
		
		private const _TOP_LEFT:VVector2 = new VVector2();
		private const _TOP_RIGHT:VVector2 = new VVector2();
		private const _BOTTOM_RIGHT:VVector2 = new VVector2();
		private const _BOTTOM_LEFT:VVector2 = new VVector2();
		
		private const _TOP_LEFT_TRANSFORMED:VVector2 = new VVector2();
		private const _TOP_RIGHT_TRANSFORMED:VVector2 = new VVector2();
		private const _BOTTOM_RIGHT_TRANSFORMED:VVector2 = new VVector2();
		private const _BOTTOM_LEFT_TRANSFORMED:VVector2 = new VVector2();
		
		public var parent:DisplayObjectContainer2D;
		public var next:DisplayObject2D;
		public var prev:DisplayObject2D;
		public var name:String;
		
		private var _x:Number = 0;
		private var _y:Number = 0;
		private var _rotation:Number = 0;
		private var _width:Number = 0;
		private var _height:Number = 0;
		private var _stage:Stage2D;
		private var _stageVertices:Vector.<Number>;
		private var _stageColors:Vector.<Number>;
		private var _stageUVs:Vector.<Number>;
		private var _stageIndices:Vector.<uint>;
		private var _index:uint = 0;
		private var _transformChanged:Boolean = false;
		private var _totalDescendants:uint = 0;
		
		private var _verticesChanged:Boolean = false;
		private var _uvsChanged:Boolean = false;
		private var _colorsChanged:Boolean = false;
		
		public function DisplayObject2D() {
			_COLORS[0] = 1;
			_COLORS[1] = 1;
			_COLORS[2] = 1;
			_COLORS[3] = 1;
			_COLORS[4] = 1;
			_COLORS[5] = 1;
			_COLORS[6] = 1;
			_COLORS[7] = 1;
			_COLORS[8] = 1;
			_COLORS[9] = 1;
			_COLORS[10] = 1;
			_COLORS[11] = 1;
			
			_UVS[0] = 0;
			_UVS[1] = 0;
			_UVS[2] = 0;
			_UVS[3] = 0;
			_UVS[4] = 0;
			_UVS[5] = 0;
			_UVS[6] = 0;
			_UVS[7] = 0;
		}
		
		public function addData():void
		{
			var ind:uint = index;
			_stageVertices.splice(ind * 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
			_stageColors.splice(ind * 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
			_stageUVs.splice(ind * 8, 0, 0, 0, 0, 0, 0, 0, 0, 0);
			
			var totalEntries:uint = _stageIndices.length / 6;
			var offset:uint = 0;
			
			var tl:uint = 0 + totalEntries * 4;
			var tr:uint = 1 + totalEntries * 4;
			var br:uint = 2 + totalEntries * 4;
			var bl:uint = 3 + totalEntries * 4;
			_stageIndices.push(tl, tr, bl, tr, br, bl);
			
			for (var childIndex:uint = 0; childIndex < _totalDescendants; childIndex++) {
				ind++;
				offset += 4;
				
				_stageVertices.splice(ind * 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
				_stageColors.splice(ind * 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
				_stageUVs.splice(ind * 8, 0, 0, 0, 0, 0, 0, 0, 0, 0);
				
				tl = 0 + totalEntries * 4 + offset;
				tr = 1 + totalEntries * 4 + offset;
				br = 2 + totalEntries * 4 + offset;
				bl = 3 + totalEntries * 4 + offset;
				_stageIndices.push(tl, tr, bl, tr, br, bl);
			}
			
			_stage.verticesChanged = true;
			_stage.colorsChanged = true;
			_stage.uvsChanged = true;
			_stage.indicesChanged = true;
		}
		
		public function setColors(colors:Vector.<Number>):void {
			_COLORS[0] = colors[0];
			_COLORS[1] = colors[1];
			_COLORS[2] = colors[2];
			_COLORS[3] = colors[3];
			_COLORS[4] = colors[4];
			_COLORS[5] = colors[5];
			_COLORS[6] = colors[6];
			_COLORS[7] = colors[7];
			_COLORS[8] = colors[8];
			_COLORS[9] = colors[9];
			_COLORS[10] = colors[10];
			_COLORS[11] = colors[11];
			
			_colorsChanged = true;
		}
		
		public function setSize(w:Number, h:Number, registrationPoint:VVector2 = null):void {
			_width = w;
			_height = h;
			
			var registrationX:Number = 0;
			var registrationY:Number = 0;
			
			if (registrationPoint != null) {
				registrationX = registrationPoint.x;
				registrationY = registrationPoint.y;
			}
			
			_TOP_LEFT.x = -registrationX;
			_TOP_LEFT.y = -registrationY;
			
			_TOP_RIGHT.x = _width - registrationX;
			_TOP_RIGHT.y = -registrationY;
			
			_BOTTOM_RIGHT.x = _width - registrationX;
			_BOTTOM_RIGHT.y = _height - registrationY;
			
			_BOTTOM_LEFT.x = -registrationX;
			_BOTTOM_LEFT.y = _height - registrationY;
			
			_TRANSFORMED_VERTICES[0] = _TOP_LEFT.x;
			_TRANSFORMED_VERTICES[1] = _TOP_LEFT.y;
			_TRANSFORMED_VERTICES[2] = 1;
			
			_TRANSFORMED_VERTICES[3] = _TOP_RIGHT.x;
			_TRANSFORMED_VERTICES[4] = _TOP_RIGHT.y;
			_TRANSFORMED_VERTICES[5] = 1;
			
			_TRANSFORMED_VERTICES[6] = _BOTTOM_RIGHT.x;
			_TRANSFORMED_VERTICES[7] = _BOTTOM_RIGHT.y;
			_TRANSFORMED_VERTICES[8] = 1;
			
			_TRANSFORMED_VERTICES[9] = _BOTTOM_LEFT.x;
			_TRANSFORMED_VERTICES[10] = _BOTTOM_LEFT.y;
			_TRANSFORMED_VERTICES[11] = 1;
			
			_verticesChanged = true;
			_colorsChanged = true;
			_uvsChanged = true;
		}
		
		public function transform():void {
			if (parent) {
				_MODEL_TRANSFORM.multiplyByMatrixCopyTo(parent.worldTransform, _WORLD_TRANSFORM);
			}else {
				_MODEL_TRANSFORM.copyTo(_WORLD_TRANSFORM);
			}
			
			_TOP_LEFT.multiplyByMatrixCopyTo(_WORLD_TRANSFORM, _TOP_LEFT_TRANSFORMED);
			_TOP_RIGHT.multiplyByMatrixCopyTo(_WORLD_TRANSFORM, _TOP_RIGHT_TRANSFORMED);
			_BOTTOM_RIGHT.multiplyByMatrixCopyTo(_WORLD_TRANSFORM, _BOTTOM_RIGHT_TRANSFORMED);
			_BOTTOM_LEFT.multiplyByMatrixCopyTo(_WORLD_TRANSFORM, _BOTTOM_LEFT_TRANSFORMED);
			
			_TRANSFORMED_VERTICES[0] = _TOP_LEFT_TRANSFORMED.x;
			_TRANSFORMED_VERTICES[1] = _TOP_LEFT_TRANSFORMED.y;
			_TRANSFORMED_VERTICES[2] = 1;
			
			_TRANSFORMED_VERTICES[3] = _TOP_RIGHT_TRANSFORMED.x;
			_TRANSFORMED_VERTICES[4] = _TOP_RIGHT_TRANSFORMED.y;
			_TRANSFORMED_VERTICES[5] = 1;
			
			_TRANSFORMED_VERTICES[6] = _BOTTOM_RIGHT_TRANSFORMED.x;
			_TRANSFORMED_VERTICES[7] = _BOTTOM_RIGHT_TRANSFORMED.y;
			_TRANSFORMED_VERTICES[8] = 1;
			
			_TRANSFORMED_VERTICES[9] = _BOTTOM_LEFT_TRANSFORMED.x;
			_TRANSFORMED_VERTICES[10] = _BOTTOM_LEFT_TRANSFORMED.y;
			_TRANSFORMED_VERTICES[11] = 1;
			
			_verticesChanged = true;
		}
		
		public function updateData():void {
			if (_transformChanged) {
				transform();
				_transformChanged = false;
			}
			
			if (_verticesChanged) {
				_verticesChanged = false;
				
				var firstVertexIndexOffset:uint = _index * 12;
				
				_stageVertices[firstVertexIndexOffset] = _TRANSFORMED_VERTICES[0];
				_stageVertices[firstVertexIndexOffset + 1] = _TRANSFORMED_VERTICES[1];
				_stageVertices[firstVertexIndexOffset + 2] = _TRANSFORMED_VERTICES[2];
				_stageVertices[firstVertexIndexOffset + 3] = _TRANSFORMED_VERTICES[3];
				_stageVertices[firstVertexIndexOffset + 4] = _TRANSFORMED_VERTICES[4];
				_stageVertices[firstVertexIndexOffset + 5] = _TRANSFORMED_VERTICES[5];
				_stageVertices[firstVertexIndexOffset + 6] = _TRANSFORMED_VERTICES[6];
				_stageVertices[firstVertexIndexOffset + 7] = _TRANSFORMED_VERTICES[7];
				_stageVertices[firstVertexIndexOffset + 8] = _TRANSFORMED_VERTICES[8];
				_stageVertices[firstVertexIndexOffset + 9] = _TRANSFORMED_VERTICES[9];
				_stageVertices[firstVertexIndexOffset + 10] = _TRANSFORMED_VERTICES[10];
				_stageVertices[firstVertexIndexOffset + 11] = _TRANSFORMED_VERTICES[11];
				
				stage.verticesChanged = true;
			}
			
			if (_colorsChanged) {
				_colorsChanged = false;
				
				var firstColorIndexOffset:uint = _index * 12;
				
				_stageColors[firstColorIndexOffset] = _COLORS[0];
				_stageColors[firstColorIndexOffset + 1] = _COLORS[1];
				_stageColors[firstColorIndexOffset + 2] = _COLORS[2];
				_stageColors[firstColorIndexOffset + 3] = _COLORS[3];
				_stageColors[firstColorIndexOffset + 4] = _COLORS[4];
				_stageColors[firstColorIndexOffset + 5] = _COLORS[5];
				_stageColors[firstColorIndexOffset + 6] = _COLORS[6];
				_stageColors[firstColorIndexOffset + 7] = _COLORS[7];
				_stageColors[firstColorIndexOffset + 8] = _COLORS[8];
				_stageColors[firstColorIndexOffset + 9] = _COLORS[9];
				_stageColors[firstColorIndexOffset + 10] = _COLORS[10];
				_stageColors[firstColorIndexOffset + 11] = _COLORS[11];
				
				_stage.colorsChanged = true;
			}
			
			if (_uvsChanged) {
				_uvsChanged = false;
				
				var firstUVIndexOffset:uint = _index * 8;
				
				_stageUVs[firstUVIndexOffset] = _UVS[0];
				_stageUVs[firstUVIndexOffset + 1] = _UVS[1];
				_stageUVs[firstUVIndexOffset + 2] = _UVS[2];
				_stageUVs[firstUVIndexOffset + 3] = _UVS[3];
				_stageUVs[firstUVIndexOffset + 4] = _UVS[4];
				_stageUVs[firstUVIndexOffset + 5] = _UVS[5];
				_stageUVs[firstUVIndexOffset + 6] = _UVS[6];
				_stageUVs[firstUVIndexOffset + 7] = _UVS[7];
				
				stage.uvsChanged = true;
			}
		}
		
		public function updateIndex():void
		{
			if (prev != null) {
				index = prev.index + prev._totalDescendants + 1;
			}else if (parent != null) {
				index = parent.index + 1;
			}
			
			if (next != null) {
				next.updateIndex();
			}
			
			var par:DisplayObjectContainer2D = parent;
			while (par) {
				var parBro:DisplayObject2D = par.next;
				while (parBro) {
					parBro.updateIndex();
					parBro = parBro.next;
				}
				par = par.parent;
			}
		}
		
		public function setUVBox(uvBox:UVBox):void {
			_UVS[0] = uvBox.topLeftX;
			_UVS[1] = uvBox.topLeftY;
			_UVS[2] = uvBox.bottomRightX;
			_UVS[3] = uvBox.topLeftY;
			_UVS[4] = uvBox.bottomRightX;
			_UVS[5] = uvBox.bottomRightY;
			_UVS[6] = uvBox.topLeftX;
			_UVS[7] = uvBox.bottomRightY;
			
			_uvsChanged = true;
		}
		
		public function set color(value:uint):void
		{
			var red:Number = ((value & 0x00FF0000) >> 16) / 0xFF;
			var green:Number = ((value & 0x0000FF00) >> 8) / 0xFF;
			var blue:Number = (value & 0x000000FF) / 0xFF;
			
			_COLORS[0] = red;
			_COLORS[1] = green;
			_COLORS[2] = blue;
			_COLORS[3] = red;
			_COLORS[4] = green;
			_COLORS[5] = blue;
			_COLORS[6] = red;
			_COLORS[7] = green;
			_COLORS[8] = blue;
			_COLORS[9] = red;
			_COLORS[10] = green;
			_COLORS[11] = blue;
		}
		
		public function set index(value:uint):void {
			_index = value;
		}
		
		public function get index():uint {
			return _index;
		}
		
		public function set rotation(value:Number):void {
			if (_rotation == value) {
				return;
			}
			
			_rotation = value;
			
			_MODEL_TRANSFORM.setupRotation(_rotation);
			_transformChanged = true;
		}
		
		public function get rotation():Number {
			return _rotation;
		}
		
		public function set stage(s:Stage2D):void
		{
			_stage = s;
			_stageVertices = _stage.ALL_VERTICES;
			_stageColors = _stage.ALL_COLORS;
			_stageUVs = _stage.ALL_UVS;
			_stageIndices = _stage.ALL_INDICES;
		}
		
		public function get stage():Stage2D
		{
			return _stage;
		}
		
		public function get totalDescendants():uint {
			return _totalDescendants;
		}
		
		public function set totalDescendants(value:uint):void {
			_totalDescendants = value;
		}
		
		public function get worldTransform():VMatrix2D {
			return _WORLD_TRANSFORM;
		}
		
		public function set x(value:Number):void {
			if (_x == value) {
				return;
			}
			
			_x = value;
			
			_MODEL_TRANSFORM.setupTranslation(_x, _y);
			_transformChanged = true;
		}
		
		public function get x():Number {
			return _x;
		}
		
		public function set y(value:Number):void {
			if (_y == value) {
				return;
			}
			
			_y = value;
			
			_MODEL_TRANSFORM.setupTranslation(_x, _y);
			_transformChanged = true;
		}
		
		public function get y():Number {
			return _y;
		}
	}
}